package net.openvision.music.composition;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

public class ElementValue {
	public static final byte UNDEFINED = Byte.MAX_VALUE;

	private BitSet bitValue;
	private byte[] values;
	private int firstDefined;
	private boolean isComplete;

	private void _init(byte... values) {
		List<Byte> list = new ArrayList<Byte>(values.length);
		isComplete = true;
		firstDefined = -1;
		for (int i = 0; i < values.length; i++) {
			if (values[i] == UNDEFINED) {
				isComplete = false;
			} else {
				if (firstDefined < 0) {
					firstDefined = i;
				}
				list.add(values[i]);
			}
		}
		Collections.sort(list);
		for (int i = 0, j = 0; i < values.length; i++) {
			if (values[i] != UNDEFINED) {
				values[i] = list.get(j++);
			}
		}
		this.values = values;
		bitValue = new BitSet(128);
		for (byte i : values)
			if (i != UNDEFINED)
				bitValue.set(i);
	}

	public ElementValue(byte... values) {
		_init(values);
	}

	public ElementValue(Collection<Byte> values) {
		byte[] val = new byte[values.size()];
		int j = 0;
		for (byte i : values) {
			val[j++] = i;
		}
		_init(val);
	}

	public boolean contains(ElementValue e) {
		BitSet tmp = (BitSet) bitValue.clone();
		tmp.or(e.bitValue);
		return (tmp.equals(bitValue));
	}

	public byte getValue(int index) {
		return values[index];
	}

	/**
	 * Gets the first defined value.
	 * 
	 * @return the first defined value.
	 */
	public byte getFirstValue() {
		return values[firstDefined];
	}

	/**
	 * Gets the index of the first defined value.
	 * 
	 * @return the index of the first defined value.
	 */
	public int getFirstIndex() {
		return firstDefined;
	}

	public byte getNextValue(int index) {
		for (int i = index; i < values.length; i++) {
			if (values[i] != UNDEFINED) {
				return values[i];
			}
		}
		return UNDEFINED;
	}

	public byte getPrevoiusValue(int index) {
		for (int i = index; i >= 0; i--) {
			if (values[i] != UNDEFINED) {
				return values[i];
			}
		}
		return UNDEFINED;
	}

	public int getIndexOf(byte value) {
		return Arrays.binarySearch(values, value);
	}

	public int getSize() {
		return values.length;
	}

	public boolean isComplete() {
		return isComplete;
	}

	@Override
	public boolean equals(Object o) {
		if (!(o instanceof ElementValue))
			return false;
		ElementValue e = (ElementValue) o;
		return bitValue.equals(e.bitValue);
	}

	@Override
	public int hashCode() {
		return bitValue.hashCode();
	}

	@Override
	public String toString() {
		return Arrays.toString(values);
	}

	BitSet getInternal() {
		return bitValue;
	}
	
	byte[] getBytes() {
		return values;
	}

	public ElementValue setValue(int index, byte value) {
		byte[] v = Arrays.copyOf(values, values.length);
		v[index] = value;
		return new ElementValue(v);
	}
}
